package com.tfb.web.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Timer;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.tfb.service.UserAssessmentI;
import com.tfb.service.utility.DeviceTask;
import com.tfb.rong.*;
import static com.tfb.service.utility.AppConst.USER_DB_ID;
import com.tfb.service.utility.HttpUtil;
import com.tfb.service.utility.Utils;
import com.tlr.device.service.DeviceDatabaseService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Servlet implementation class AssessmentServlet
 */
public class AssessmentServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private UserAssessmentI userservice = null;
    static Log log = LogFactory.getLog(AssessmentServlet.class);
    ObjectMapper mapper = null;
    private DeviceDatabaseService deviceService;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public AssessmentServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @param request
     * @param response
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);

    }

    List<String> parsePathInfo(String pathInfo, String encoding) {
        List<String> uriList = new ArrayList<>();
        if (null == pathInfo) {
            return uriList;
        }
        StringTokenizer tokens = new StringTokenizer(pathInfo, "/");
        while (tokens.hasMoreTokens()) {
            String token = tokens.nextToken();

            try {
                token = URLDecoder.decode(token, "utf-8");
            } catch (Exception ex) {
            }

            uriList.add(token);
        }

        int uriSize = uriList.size();
        if (uriSize > 0) {
            String lastUri = uriList.get(uriSize - 1);
            if (lastUri.endsWith(".json")) {
                lastUri = lastUri.substring(0, uriSize - 5);
                uriList.set(uriSize - 1, lastUri);
            } else if (lastUri.endsWith(".xml")) {
                lastUri = lastUri.substring(0, uriSize - 4);
                uriList.set(uriSize - 1, lastUri);
            }
        }

        return uriList;
    }

    String getEncoding(HttpServletRequest req) {
        String contentType = req.getContentType();
        if (null == contentType) {
            return "utf-8";
        }

        int start = contentType.indexOf("charset=");
        if (start < 0) {
            return "utf-8";
        }
        String charset = contentType.substring(start + 8);

        int end = charset.indexOf(';');
        if (end >= 0) {
            charset = charset.substring(0, end);
        }

        return charset.trim();
    }

    private String parseActionName(List<String> uriList) {
        if (null == uriList) {
            return null;
        }

        int uriSize = uriList.size();
        for (int i = 0; uriSize >= 2 && i < uriSize - 1; i++) {
            if (uriList.get(i).equalsIgnoreCase("action")) {
                String action = uriList.get(i + 1);
                if (null != action) {
                    return action;
                }
            }
        }

        return null;
    }

    AssessmentBean readAssessmentBean(HttpServletRequest req, HttpServletResponse resp, String strjson) {
        AssessmentBean ab = null;
        try {
            if (strjson != null) {
                ab = mapper.readValue(strjson, AssessmentBean.class);
            } else {
                ab = mapper.readValue(req.getInputStream(), AssessmentBean.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ab;
    }

    void createAssessment(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        AssessmentBean ab = readAssessmentBean(req, resp, strjson);
        if (ab == null) {
            writeResponse(resp, "pasre json fail when createassesment", 2);
            return;
        }
        //bean to database,
        //first get the id
        int id = userservice.getNextID("assessment_id");
        ab.setId(String.format("AM%06d", id));
        ab.setStatus(0);
        ab.setLastmodify(new Date());
        ab.setCreatedby(userinfo.getId());
        userservice.AddAssessment(ab);

        writeResponse(resp, "create assessment successfully", 0, HttpServletResponse.SC_OK, ab);
    }

    void queryAssessment(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        AssessmentBean ab = readAssessmentBean(req, resp, strjson);
        if (ab == null) {
            writeResponse(resp, "pasre json fail when queryassesment", 2);
            return;
        }
        AssessmentCommentBean acb = userservice.queryAssessMentDetail(ab.getId());
        writeResponse2(resp, "annotate assessment successfully", 0, HttpServletResponse.SC_OK, acb);
    }

    void getAssessment(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        AssessmentBean ab = readAssessmentBean(req, resp, strjson);
        if (ab == null) {
            writeResponse(resp, "pasre json fail when geteassesment", 2);
            return;
        }
        AssessmentBean acb = userservice.queryAssessmentwithoucomments(ab.getId());
        writeResponse(resp, "get assessment successfully", 0, HttpServletResponse.SC_OK, acb);
    }

    void modifyAssessment(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        AssessmentBean ab = readAssessmentBean(req, resp, strjson);

        if (ab == null) {
            writeResponse(resp, "pasre json fail when modifyassesment", 2);
            return;
        }
        ab.setLastmodify(new Date());
        int ret = userservice.updateAssessment(ab);
        writeResponse(resp, "modify Assessment successfully", ret, HttpServletResponse.SC_OK, null);
    }

    void annotateAssessment(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        AssessmentCommentBean ab = null;
        try {
            if (strjson != null) {
                ab = mapper.readValue(strjson, AssessmentCommentBean.class);
            } else {
                ab = mapper.readValue(req.getInputStream(), AssessmentCommentBean.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (ab == null || ab.getAssessmentBean() == null || ab.getAllComments() == null) {
            writeResponse(resp, "pasre json fail when AssessmentCommentBean", 2);
            return;
        }
        // first update the id
        for (AssessmentComments ac : ab.getAllComments()) {
            ac.setAssessmentId(ab.getAssessmentBean().getId());
        }
        ab.getAssessmentBean().setLastmodify(new Date());
        userservice.addAssessmentComments(ab);
        writeResponse(resp, "annotate assessment successfully", 0, HttpServletResponse.SC_OK, null);
    }

    void listAssessment(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        Assessment record = new Assessment();
        record.setCreatedby(userinfo.getId());
        List<Assessment> ls = userservice.selectMyAssessment(record);
        try (PrintWriter out = resp.getWriter()) {
            //  mapper.writeValue(resp.getOutputStream(), ls);
            String json = mapper.writeValueAsString(ls);
            System.out.println(json);
            out.println(json);
            out.flush();
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
        }
    }

    void register(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        try {
            Users ab = mapper.readValue(req.getInputStream(), Users.class);

            if (ab == null) {
                log.error("pasre json fail when register user");
                writeResponse(resp, "pasre json fail when register user", -2);
                return;
            }
            String checkUnique = "";
            Users query = new Users();
            query.setTel(ab.getTel());
            if (deviceService.getUserMapper().selectUserInfo(query) != null) {
                checkUnique += "电话已经被使用";
            }
            query.setTel(null);
            query.setEmail(ab.getEmail());
            if (deviceService.getUserMapper().selectUserInfo(query) != null) {
                checkUnique += "邮箱已经被使用";
            }
            if (!checkUnique.isEmpty()) {
                log.error(checkUnique);
                writeResponse(resp, checkUnique, 300, HttpServletResponse.SC_OK, null);
            }

            ab.setId(String.format("U%06d", userservice.getNextID("user_id")));
            ab.setEmployeetype(Users.NONE);
            ab.setKeyword(Users.NORMAL_KEYWORDS);
            ab.setType(Users.EMPLOYEE);
            if (userservice.insertUserInfo(ab) == 1) {

                writeResponse(resp, "okay", 0, HttpServletResponse.SC_OK, null);
            } else {
                writeResponse(resp, "fail", 0, HttpServletResponse.SC_OK, null);
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
        }
    }

    void updatepolicy(HttpServletRequest req, HttpServletResponse resp, String strjson, Users userinfo) {
        try {
            Users ab = mapper.readValue(req.getInputStream(), Users.class);

            if (ab == null) {
                log.error("pasre json fail when register user");
                writeResponse(resp, "pasre json fail when register user", -2);
                return;
            }

            if (ab.getPolicyread() != null && (userinfo.getPolicyread() == null || userinfo.getPolicyread() < ab.getPolicyread())) {
                userinfo.setPolicyread(ab.getPolicyread());
                userservice.updateUser(userinfo);

            }
            writeResponse(resp, "ok", 0, HttpServletResponse.SC_OK, null);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void handle(HttpServletRequest req, HttpServletResponse resp, String action, List<String> uriList, String strjson, Users userinfo) {
        if (action.equalsIgnoreCase("register")) {
            register(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("createassessment")) {
            createAssessment(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("queryassessment")) {
            queryAssessment(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("getassessment")) {
            getAssessment(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("listassessment")) {
            listAssessment(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("modifyassessment")) {
            modifyAssessment(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("annotateassessment")) {
            annotateAssessment(req, resp, strjson, userinfo);
        } else if (action.equalsIgnoreCase("updatepolicy")) {
            updatepolicy(req, resp, strjson, userinfo);
        } else {
            writeResponse(resp, "un-support aciton", -2);
        }
    }

    void writeResponse(HttpServletResponse response, String msg, int iret) {
        // response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        Response rs = new Response();
        rs.setReturncode(iret);
        rs.setMessage(msg);

        String json;
        try {
            json = mapper.writeValueAsString(rs);
            //mapper.writeValue(response.getOutputStream(), rs);
            PrintWriter out = response.getWriter();
            out.println(json);
            //out.flush();
            //out.close();
        } catch (JsonGenerationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JsonMappingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        response.setHeader("Connection", "close");
    }

    void writeResponse2(HttpServletResponse response, String msg, int ret, int httpcode, AssessmentCommentBean ab) {
        // response.setStatus(httpcode);
        Response rs = new Response();
        rs.setReturncode(ret);
        rs.setMessage(msg);
        rs.setAssessment(null);
        rs.setAssessmentwithcoments(ab);
        String json;
        try {
            //mapper.writeValue(response.getOutputStream(), rs);
            json = mapper.writeValueAsString(rs);
            System.out.println(json);
            PrintWriter out = response.getWriter();
            out.println(json);
            out.flush();
            //out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    void writeLoginResponse(HttpServletResponse response, String msg, int ret, int httpcode, Users userinfo) {
        // response.setStatus(httpcode);
        Response rs = new Response();
        rs.setReturncode(ret);
        rs.setMessage(msg);
        rs.setAssessment(null);
        rs.setAssessmentwithcoments(null);
        rs.setDevice(null);
        String strpwd = userinfo.getPassword();
        userinfo.setPassword(null);
        //only care userinfo
        rs.setUserinfo(userinfo);
        String json;
        try {
            json = mapper.writeValueAsString(rs);
            PrintWriter out = response.getWriter();
            //  mapper.writeValue(response.getOutputStream(), rs);
            out.println(json);
            out.flush();
            //out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        userinfo.setPassword(strpwd);
    }

    void writeResponse(HttpServletResponse response, String msg, int ret, int httpcode, AssessmentBean ab) {
        // response.setStatus(httpcode);
        Response rs = new Response();
        rs.setReturncode(ret);
        rs.setMessage(msg);
        rs.setAssessment(ab);
        rs.setAssessmentwithcoments(null);
        String json;
        try {
            json = mapper.writeValueAsString(rs);
            PrintWriter out = response.getWriter();
            //  mapper.writeValue(response.getOutputStream(), rs);
            out.println(json);
            out.flush();
            //out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean doTest(HttpServletRequest request, HttpServletResponse response, String action) {
        if (request.getParameter("action") != null && request.getParameter("jsondata") != null && action != null && action.compareToIgnoreCase("test") == 0) {
            String straction = request.getParameter("action");
            String strjson = request.getParameter("jsondata");
            handle(request, response, straction, null, strjson, null);
            /* try {
             //response.sendRedirect("/rong/result.jsp");
             } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             }*/
            return true;
        }
        return false;
    }

    private Users getUserInfo(HttpServletRequest req, HttpServletResponse resp, HttpSession httpSession, String straction) {
        try {
            if (straction != null && straction.compareToIgnoreCase("login") == 0) {
                Users ab = null;
                ab = mapper.readValue(req.getInputStream(), Users.class);
                if (ab == null) {
                    log.error("pasre json fail when logincheck");
                    return null;
                }
                // bean to database,
                Users userinfo = userservice.selectUserInfo(new Credentials(ab.getName(), ab.getPassword()));
                if (userinfo != null) {
                    httpSession.setAttribute("userinfo", userinfo);
                } else {
                    String sessionUserDBID = httpSession.getAttribute(USER_DB_ID).toString();
                    if (sessionUserDBID == null) {
                        sessionUserDBID = HttpUtil.getCookieUserDBID(req);
                    }
                    Users user = new Users();
                    user.setId(sessionUserDBID);
                    userinfo = userservice.selectBindUserInfo(user);
                }
                return userinfo;
            } else {
                Users userinfo = (Users) httpSession.getAttribute("userinfo");
                if (userinfo == null) {
                    String sessionUserDBID = httpSession.getAttribute(USER_DB_ID).toString();
                    if (sessionUserDBID == null) {
                        sessionUserDBID = HttpUtil.getCookieUserDBID(req);
                    }
                    Users user = new Users();
                    user.setId(sessionUserDBID);
                    userinfo = userservice.selectBindUserInfo(user);
                }
                return userinfo;
            }
        } catch (Exception e) {
            log.error(Utils.getFullException(e));
        }

        return null;
    }

    /**
     * @param request
     * @param response
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //if for test, just return 
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        // response.setContentType("text/json;charset=utf-8");
        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();
        String servletPath = request.getServletPath();
        String pathInfo = uri.substring(contextPath.length() + servletPath.length());
        log.info("request uri is:" + uri + " contextpath is:" + contextPath + " servletpath is:" + servletPath + " pathinof is:" + pathInfo);
        System.out.println("request uri is:" + uri + " contextpath is:" + contextPath + " servletpath is:" + servletPath + " pathinof is:" + pathInfo);
        //first check the action is available or not
        HttpSession httpSession = request.getSession(true);
        List<String> uriList = parsePathInfo(pathInfo, getEncoding(request));
        String straction = parseActionName(uriList);
        if (straction != null) {
            straction = straction.toLowerCase();
        }
        boolean bNeedAuthorization = true;
        try {
            // TODO Skip authentication temporary
            Users userinfo = getUserInfo(request, response, httpSession, straction);
            //if (!checkUserInfo(httpSession, response, straction, userinfo)) {
            //return;
            //}

            handleConnection(request, response);
            handleTimeout(httpSession);

            if (doTest(request, response, straction)) {
                return;
            }
            // Test
            //userinfo = new Users();
            //userinfo.setName("test");
            handle(request, response, straction, uriList, null, userinfo);

        } catch (Exception ex) {
            log.warn(ex.getMessage(), ex);
            writeResponse(response, ex.toString(), -1);
        }
    }

    private void handleTimeout(HttpSession httpSession) {
        int timeoutLimit = 10 * 60;
        if (timeoutLimit == 0) {
            // use Tomcat's default httpSession timeout
            timeoutLimit = httpSession.getMaxInactiveInterval();// in seconds
            {
                log.debug("webservices_sessiontimeout is not set");
                log.debug("HTTP session timeout will be used for the REST webservices API session timeout.");
            }
        }

        httpSession.setMaxInactiveInterval(timeoutLimit);
    }

    @SuppressWarnings("unused")
    private void handleConnection(HttpServletRequest request, HttpServletResponse response) {
        HeaderNetInfo connInfo = new HeaderNetInfo(request);
        boolean keepAlive = connInfo.getKeepAlive();
        if (false) {
            response.addHeader("Keep-Alive", "timeout=1200000, max=1000");
            response.addHeader("Connection", "Keep-Alive");
        } else {
            response.addHeader("Connection", "close");
        }
    }

    @SuppressWarnings("unused")
    private boolean checkUserInfo(HttpSession httpSession, HttpServletResponse response, String straction,
                                  Users userinfo) {
        if (null == userinfo || straction == null) {
            log.info("fail to authorizated! didn't login or action is not specialed!");

            writeResponse(response, "fail to authorizated!", -2);
            //request.getRequestDispatcher("/users.html").forward(request, response);
            return false;
        } else if (straction != null && (straction.equalsIgnoreCase("login"))) {
            if (userinfo.getType() == Users.CUSTOMER) {
                writeLoginResponse(response, "#/customer-profile", 0, HttpServletResponse.SC_OK, userinfo);
                //request.getRequestDispatcher("/users.html#/customer-profile").forward(request, response);
            } else {
                writeLoginResponse(response, "#/employee-profile", 0, HttpServletResponse.SC_OK, userinfo);
                //request.getRequestDispatcher("/users.html#/employee-profile").forward(request, response);
            }
            return false;
        } else if (!userinfo.getKeyword().contains("," + straction + ",") && !userinfo.getKeyword().contains("*")) {
            log.info("action is not allowed!");

            writeResponse(response, "fail to authorizated!action is not allowed", -3);
            //request.getRequestDispatcher("/users.html").forward(request, response);
            return false;
        } else {
            //put it in our mgr
            if (SessionMgr.getApiSession(httpSession.getId()) == null) {
                SessionMgr.putApiSession(httpSession.getId(), httpSession);
            }
            log.info("pass the authorization!");
        }
        return true;
    }

    private void StartOneTimeByInterval(String strInterval, int type) {
        float lInterval = Float.parseFloat(strInterval); //this should be hours
        DeviceTask devicetask = new DeviceTask(this, type, null);
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(devicetask, 0, Math.round(lInterval * 60 * 60 * 1000));
    }

    private void StartOneTimer(String strStart, String strInterval, int type) {
        Date dt = new Date();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd ");
        String date = formatter.format(dt);
        date += strStart;
        if (strStart.indexOf(':') == strStart.lastIndexOf(':')) {
            date += ":00";
        }
        log.info("start.time  is :" + date);
        formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        ParsePosition pos = new ParsePosition(0);
        Date datetime = formatter.parse(date, pos);
        long lstart = datetime.getTime() - dt.getTime();
        if (lstart < 0) {
            lstart = 0; //start now
        }
        float lInterval = Float.parseFloat(strInterval); //this should be hours
        DeviceTask devicetask = new DeviceTask(this, type, null);
        Timer timer = new Timer();
        timer.schedule(devicetask, lstart, Math.round(lInterval * 60 * 60 * 1000));
    }

    public void startDataFetchTimer() {
        ServletContext sc = getServletConfig().getServletContext();
        String strStart = sc.getInitParameter("device.airbox.fetchdata.start.time");
        String strInterval = sc.getInitParameter("device.airbox.fetchdata.interval");
        //StartOneTimer(strStart, strInterval, DeviceTask.DEVICE_AIRBOX);
        StartOneTimeByInterval(strInterval, DeviceTask.DEVICE_AIRBOX);

        strStart = sc.getInitParameter("device.airclean.fetchdata.start.time");
        strInterval = sc.getInitParameter("device.airclean.fetchdata.interval");
        //StartOneTimer(strStart, strInterval, DeviceTask.DEVICE_AIRCLEAN);

        // Start eban last data at once when start up the servlet
        StartOneTimeByInterval(strInterval, DeviceTask.DEVICE_AIRCLEAN);

        strStart = sc.getInitParameter("device.eban.fetchdata.start.time");
        strInterval = sc.getInitParameter("device.eban.fetchdata.interval");
        //StartOneTimer(strStart, strInterval, DeviceTask.DEVICE_EBAN);
        StartOneTimeByInterval(strInterval, DeviceTask.DEVICE_EBAN);

    }

    @Override
    public void init() throws ServletException {
        ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        if (userservice == null) {
            userservice = (UserAssessmentI) ac.getBean("userAssessmentService");
        }
        if (deviceService == null) {
            deviceService = (DeviceDatabaseService) ac.getBean("deviceDatabaseService");
        }
        mapper = new ObjectMapper();
        mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(Inclusion.NON_NULL);
    }
}
